<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      canvas {
        display: block;
        margin: 10px auto;
        border: 1px solid orange;
      }
    </style>
  </head>

  <body>
    <canvas id="canvas" width="600" height="500">上古浏览器???</canvas>
    <script>
      // 获取实际像素比例
      function getPixelRatio() {
        return window.devicePixelRatio || 1
      }
      /** @type {HTMLCanvasElement} */
      const canvas = document.getElementById('canvas')
      const ctx = canvas.getContext('2d')
      /**
       * 高清绘制的处理方式
       *
       * 在 css 里将宽高设置为原来的大小
       * 按比例放大 canvas
       * 考虑到内容的缩放，因此也需要将 ctx 缩放
       */
      const ratio = getPixelRatio()

      canvas.style.width = canvas.width + 'px'
      canvas.style.height = canvas.height + 'px'

      canvas.width = canvas.width * ratio
      canvas.height = canvas.height * ratio

      class Ball {
        constructor(option) {
          this.ctx = option.ctx
          this.title = option.title
          this.canvas = option.canvas
          this.r = Math.random() * 40 + 10
          this.w = canvas.clientWidth * ratio
          this.h = canvas.clientHeight * ratio
          this.x = Math.random() * (this.w - this.r * 2) + this.r
          this.y = Math.random() * (this.h - this.r * 2) + this.r
          this.Xspeed = Math.random() * 4 + 4
          this.Yspeed = Math.random() * 2 + 2
          this.color = parseInt(Math.random() * 0xffffff).toString(16)
          this.init()
        }

        init() {
          this.drawBall()
          this.drawText()
          this.run()
        }

        drawBall() {
          ctx.beginPath()
          ctx.fillStyle = '#' + this.color
          ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2)
          ctx.fill()
          ctx.closePath()
        }

        drawText() {
          ctx.beginPath()
          ctx.fillStyle = '#' + this.color
          ctx.textAlign = 'center'
          ctx.font = `bold ${this.r}px 微软雅黑`
          ctx.fillText(this.title, this.x, this.y + this.r * 2)
          ctx.closePath()
        }
        drawLine(x, y) {
          ctx.beginPath()
          ctx.strokeStyle = '#' + this.color
          ctx.lineWidth = 1
          ctx.moveTo(this.x, this.y)
          ctx.lineTo(x, y)
          ctx.stroke()
          ctx.closePath()
        }

        run() {
          if (this.x + this.r >= this.w || this.x - this.r <= 0) {
            this.Xspeed = -this.Xspeed
          }
          if (this.y + this.r * 2 >= this.h || this.y - this.r <= 0) {
            this.Yspeed = -this.Yspeed
          }
          this.x += this.Xspeed
          this.y += this.Yspeed
        }
      }

      let titleArr = [
        'Vue',
        'Webpack',
        'React',
        'Angluar',
        'Python',
        'Nodejs',
        'Nextjs',
        'D3js',
        'Threejs',
        'Echarts',
        'Tailwindcss'
      ]
      let ballArr = []
      titleArr.forEach(item => {
        ballArr.push(new Ball({ canvas, ctx, title: item }))
      })
      setInterval(() => {
        ctx.clearRect(0, 0, canvas.width, canvas.height)
        ballArr.forEach((ball, index) => {
          ball.init()
          for (let i = 0; i < index; i++) {
            let preBall = ballArr[i]
            ball.drawLine(preBall.x, preBall.y)
          }
        })
      }, 10)
    </script>
  </body>
</html>
